package Parser;

import java.util.ArrayList;

import org.htmlparser.*;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;

import java.io.*;



public class BioParser {
	
	
	/**
	 * Entry point of the application,
	 * call the IHM
	 * @param args not used
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
	
		new IHM();
	}
	
	/**
	 * Create a new file and write the first line
	 * @param name the file name to create
	 * @throws IOException
	 */
	public void createFile(String name) throws IOException
	{		
		File file = new File(name);
		BufferedWriter stream = new BufferedWriter(new FileWriter(file));
		stream.write("Famille;Espèce;Identifiant;Longueur;Type;Anticodon;Position;Seq;Str;Score;");
		for(int i=0; i<4; i++)
		{
			for (int j=-18; j<79; j++)
				stream.write(j + ";");
		}
		stream.write("\n");
		stream.close();
	}
	
	/**
	 * write an entry in a csv file
	 * @param fileName the file in which to write
	 * @param family the family of tRNA
	 * @param species the species of tRNA
	 * @param value the datas
	 * @throws IOException
	 */
	public void writeFile(String fileName, String family, String species, ArrayList<String> value) throws IOException
	{		
		
		BufferedWriter stream = new BufferedWriter(new FileWriter(fileName, true));
		for(int i=0; i<value.size(); i++)
		{
			stream.write(family + ";" + species + ";" + value.get(i) + "\n");
		}
		stream.close();
	}
	
	
	/**
	 * Extract tRNA of the chosen class
	 * @param workingDir the working directory in which to write files
	 * @param fileName the class name (Eukarya,Archaea,Baccteria)
	 * @throws IOException
	 */
	public void extractLinkByType(String workingDir, String fileName) throws IOException
	{
		ArrayList<String> tRNA = new ArrayList<String>();
		String header = new String();
		String family = new String();
		String species = new String();
		
		try {
			/* Download the main page and parse it in NodeList*/
			Parser parser= new Parser("http://lowelab.ucsc.edu/GtRNAdb/");
			NodeList list = parser.parse(null);
			
			/* Get all <td> contains*/
			NodeFilter typeFilter = new MyNodeFilter(3);
			NodeList mylist = list.extractAllNodesThatMatch(typeFilter, true);
			
			/* Extract chosen datas parsing html */
			for(int i=0; i<mylist.size(); i++)
			{
				Node arg0 = mylist.elementAt(i);
				/* lookup for class */
				if(arg0.getParent().getText().contains("table_header1") && arg0.getText().contains("name="))
				{
					header = arg0.getText().substring(arg0.getText().indexOf("name=")+6,arg0.getText().lastIndexOf('"'));
					if(header.contains(fileName))
						createFile(workingDir + header + ".csv");
				}
				/* the class is the one wanted */
				if(header.contains(fileName))
				{
					/* lookup for family */
					if(arg0.getParent().getText().contains("table_header2") && mylist.elementAt(i+1).getText().contains("name="))
					{
						family = mylist.elementAt(i+1).getText().substring(mylist.elementAt(i+1).getText().indexOf("name=")+6,mylist.elementAt(i+1).getText().lastIndexOf('"'));
					}
					/* building sublink, exctracting tRNA and writing datas in file */
					if(arg0.getParent().getText().contains("width=33.3%") && arg0.getText().contains("href="))
					{
						String substring = arg0.getText().substring(arg0.getText().indexOf('"')+1,arg0.getText().lastIndexOf('/'));
						species = getSpeciesName("http://lowelab.ucsc.edu/GtRNAdb/"+substring+"/"+substring+"-structs.html");
						tRNA = extractAlltRNA("http://lowelab.ucsc.edu/GtRNAdb/"+substring+"/"+substring+"-structs.html");
						writeFile(workingDir + header + ".csv", family,species, tRNA);	
					}
				}
			}
		} catch (ParserException e) {
			e.printStackTrace();
		}
	}
	/**
	 * Extract species name in a page
	 * @param link the page link in which to look for species
	 * @return
	 */
	public String getSpeciesName(String link)
	{
		String species = new String();
		try {
			Parser parser= new Parser(link);
			NodeList list = parser.parse(null);
			NodeFilter typeFilter = new MyNodeFilter(1);
			NodeList mylist = list.extractAllNodesThatMatch(typeFilter, true);
			Node arg0 = mylist.elementAt(0);
			
			species = arg0.getText().substring(arg0.getText().indexOf("Database:")+10,arg0.getText().length());
		}
		catch (ParserException e) {
			e.printStackTrace();
		}
		return species;
			
	}
	
	/**
	 * extract all entries in a web page
	 * @param link the web page to search in
	 * @return
	 */
	public ArrayList<String> extractAlltRNA(String link)
	{
		ArrayList<String> datas= new ArrayList<String>();
		try {
			/* Create the parser and filter */
			Parser parser = new Parser(link);
			NodeList list = parser.parse(null);
			/* Filter on "Length" for each entry */
			NodeFilter totoFilter = new MyNodeFilter(4);
			NodeList mylist = list.extractAllNodesThatMatch(totoFilter, true);
			/* parse all lines */
			for(int i=0;i<mylist.size();i++)
			{
				Node arg0 = mylist.elementAt(i);
				
				String identifiant = arg0.getText().substring(0,arg0.getText().indexOf("("));
				String length = arg0.getText().substring(arg0.getText().indexOf("Length:")+8,arg0.getText().indexOf(" bp"));
				String type = arg0.getText().substring(arg0.getText().indexOf("Type:")+6,arg0.getText().indexOf("Anticodon:"));
				String anticodon = arg0.getText().substring(arg0.getText().indexOf("Anticodon:")+11,arg0.getText().indexOf("Anticodon:")+14);
				String position = arg0.getText().substring(arg0.getText().indexOf("Anticodon:")+18,arg0.getText().indexOf("Anticodon:")+20);
				String seq = " ";
				String str = " ";
				if(!arg0.getText().contains("Error"))
				{
					seq = arg0.getText().substring(arg0.getText().indexOf("Seq:")+5,arg0.getText().indexOf("Str:"));
					seq = seq.toUpperCase();
					str = arg0.getText().substring(arg0.getText().indexOf("Str:")+5,arg0.getText().length());
				}
				String score = arg0.getText().substring(arg0.getText().indexOf("Score:")+7,arg0.getText().indexOf("Score:")+12);
				
				//find errors
				if(!findErrors(anticodon, seq, position))
				{
					String data = identifiant + ";" + length + ";" + type + ";" + anticodon + ";" + position + ";" + seq + ";" + str + ";" + score;
					data += createTriNucleotide(seq, Integer.parseInt(position), Integer.parseInt(length),0);
					data += createTriNucleotide(seq, Integer.parseInt(position), Integer.parseInt(length),1);
					data += createTriNucleotide(seq, Integer.parseInt(position), Integer.parseInt(length),2);
					data += createTriNucleotideX(seq, Integer.parseInt(position), Integer.parseInt(length));
					
					data = data.replaceAll(" ", "");
					data = data.replaceAll("\\t", "");
					data = data.replaceAll("\\n", "");
					datas.add(data);
				}
			}
		} catch (ParserException e) {
			e.printStackTrace();
		}
		return datas;
	}
	
	/**
	 * test for errors in entry
	 * @param anticodon
	 * @param seq
	 * @param position
	 * @return if the entry contains an error
	 */
	public boolean findErrors(String anticodon, String seq, String position)
	{
		boolean hasError = false;
		/* look for wrong letters in anticodon */
		for(int i=0; i<anticodon.length(); i++)
		{
			switch(anticodon.charAt(i))
			{
				case 'A' : break;
				case 'C' : break;
				case 'G' : break;
				case 'T' : break;
				default : hasError = true; break;						
			}
		}
		seq = seq.replaceAll(" ", "");
		seq = seq.replaceAll("\\t", "");
		seq = seq.replaceAll("\\n", "");
		anticodon = anticodon.replaceAll(" ", "");
		anticodon = anticodon.replaceAll("\\t", "");
		anticodon = anticodon.replaceAll("\\n", "");
		/* search for wrong letters in sequence */
		for(int i=0; i<seq.length(); i++)
		{
			switch(seq.charAt(i))
			{
				case 'A' : break;
				case 'C' : break;
				case 'G' : break;
				case 'T' : break;
				default : hasError = true; break;						
			}
		}
		
		/* Check that anticodon is well placed */
		if(hasError == false)
		{
			int pos = Integer.parseInt(position);
			if(seq == "ERROR" || ! seq.substring(pos-1, pos + 2).contains(anticodon))
				hasError = true;
		}
		return hasError;
	}
	
	/**
	 * Split a sequence in trinucleotide centered on anticodon
	 * @param seq
	 * @param position
	 * @param length
	 * @param variante
	 * @return
	 */
	public String createTriNucleotide(String seq, int position, int length, int variante)
	{
		String listTrinucleotides = new String();
		
		/* After a check in database, the maximum value for anticodon position is 56
		 * so we have 18 trinucleotides before */
		
		int nbCaseAvantEcrire = (int)((56-(position + variante -1))/3);
		int nbCaseAvantSave = nbCaseAvantEcrire;
		
		if(variante == 0)
			nbCaseAvantEcrire ++;

		while(nbCaseAvantEcrire > 0)
		{
			listTrinucleotides += ";";
			nbCaseAvantEcrire--;
		}
		
		/* compute position for starting split */
		int pos = ((position + variante)-1)%3;
		int nbTrinuTotal = 0;
		while(pos+3 < length)
		{
			listTrinucleotides += seq.substring(pos, pos+3) + ";";
			pos+=3;
			nbTrinuTotal++;
		}
		
		int nbCaseApresEcrire = 97 - nbTrinuTotal - nbCaseAvantSave;
		while(nbCaseApresEcrire > 0)
		{
			listTrinucleotides += ";";
			nbCaseApresEcrire--;
		}
		return listTrinucleotides;
	}
	/**
	 * Create the X codes
	 * @param seq
	 * @param position
	 * @param length
	 * @return
	 */
	public String createTriNucleotideX(String seq, int position, int length)
	{
		String listTrinucleotides = new String();
		String currentNucleo = new String();
		
		String [] triX = {"AAC", "AAT", "ACC", "ATC", "ATT", "CAG", "CTC", "CTG", "GAA", "GAC",
				"GAG", "GAT", "GCC", "GGC", "GGT", "GTA", "GTC", "GTT", "TAC", "TTC"};
		String [] triX1 = {"AAG", "ACA", "ACG", "ACT", "AGC", "AGG", "ATA", "ATG", "CCA", "CCG",
				"GCG", "GTG", "TAG", "TCA", "TCC", "TCG", "TCT", "TGC", "TTA", "TTG"};
		String [] triX2 = {"AGA", "AGT", "CAA", "CAC", "CAT", "CCT", "CGA", "CGC", "CGG", "CGT",
				"CTA", "CTT", "GCA", "GCT", "GGA", "TAA", "TAT", "TGA", "TGG", "TGT"};
		String [] triXid = {"AAA", "CCC", "GGG", "TTT"};		
		
		/* After a check in database, the maximum value for anticodon position is 56
		 * so we have 18 trinucleotides before */
		int nbCaseAvantEcrire = (int)((56-(position -1))/3) - 1;
		while(nbCaseAvantEcrire >= 0)
		{
			listTrinucleotides += ";";
			nbCaseAvantEcrire--;
		}
		
		/* compute position for starting split */
		int pos = (position-1)%3;
		while(pos+3 < length)
		{
			currentNucleo = seq.substring(pos, pos+3);
			for(int i=0; i<triX.length; i++)
			{
				if(triX[i].contains(currentNucleo))
					listTrinucleotides += "X;";
			}
			for(int i=0; i<triX1.length; i++)
			{
				if(triX1[i].contains(currentNucleo))
					listTrinucleotides += "X1;";
			}
			for(int i=0; i<triX2.length; i++)
			{
				if(triX2[i].contains(currentNucleo))
					listTrinucleotides += "X2;";
			}
			for(int i=0; i<triXid.length; i++)
			{
				if(triXid[i].contains(currentNucleo))
					listTrinucleotides += "XId;";
			}
			pos+=3;
		}
		return listTrinucleotides;
	}
}
